package com.jsbs.iam.auth.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jsbs.iam.auth.constant.RedisConstants;
import com.jsbs.iam.auth.dto.*;
import com.jsbs.iam.auth.entity.IamRole;
import com.jsbs.iam.auth.entity.IamRoleApplySystmRelation;
import com.jsbs.iam.auth.feign.IamRoleManagerFeign;
import com.jsbs.iam.auth.mapper.*;
import com.jsbs.iam.auth.param.IamApplyDetaislParam;
import com.jsbs.iam.auth.param.IamRoleQueryUserParam;
import com.jsbs.iam.auth.param.IamRoleRemoveUserParam;
import com.jsbs.iam.auth.service.IamRoleManagerService;
import com.jsbs.iam.auth.vo.IamUserInfoVo;
import com.jsbs.iam.common.core.dto.Result;
import com.jsbs.iam.common.core.utils.Json;
import com.jsbs.iam.common.core.utils.PageHelperTool;
import com.jsbs.iam.common.core.utils.StringUtils;
import com.jsbs.iam.common.redis.service.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @ClassName: IamRoleManagerServiceImpl
 * @Author: tenghuawei
 * @Date: 2022/12/26 13:42
 * @Description:
 */

@Slf4j
@Service
public class IamRoleManagerServiceImpl implements IamRoleManagerService {

    @Resource
    private RedissonClient redissonClient;

    @Autowired
    IamRoleManagerMapper iamRoleManagerMapper;

    @Autowired
    IamRoleManagerFeign iamRoleManagerFeign;

    @Autowired
    IamRoleOrgFrameworkRelationMapper iamRoleOrgFrameworkRelationMapper;

    @Autowired
    IamRoleMenuRelationMapper iamRoleMenuRelationMapper;

    @Autowired
    IamRoleButtonRelationMapper iamRoleButtonRelationMapper;

    @Autowired
    IamRoleApplySystmRelationMapper iamRoleApplySystmRelationMapper;

    @Autowired
    IamStaticUserGroupManagerMapper iamStaticUserGroupManagerMapper;

    @Value("${_env.name}")
    private String envName;

    /**
     * 功能描述：根据角色编码和角色名称，模糊查询角色列表
     * @param iamRoleCreateDto
     * @return PageInfo
     */
    @Override
    public PageInfo queryIamRole(IamRoleCreateDto iamRoleCreateDto) {
        // 初始化分页数据
        iamRoleCreateDto.init();
        PageHelper.startPage(iamRoleCreateDto.getCurrPage(), iamRoleCreateDto.getPageSize());
        List<IamRole> iamRoleList = iamRoleManagerMapper.queryIamRole(iamRoleCreateDto);
        if(CollectionUtils.isEmpty(iamRoleList)){
            log.warn("Route=>IamRoleManagerServiceImpl method=>queryIamRole 角色列表条件查询入参为：{}，未找到相关数据，建议您更换筛选条件！", Json.toJsonString(iamRoleCreateDto));
        }
        PageInfo<IamRole> pageInfo0= new PageInfo<>(iamRoleList);
        PageInfo<IamRole> pageInfo = PageHelperTool.initPageInfoObj(iamRoleCreateDto.getCurrPage(), (int) pageInfo0.getTotal(),
                iamRoleCreateDto.getPageSize(), pageInfo0);
        return pageInfo;
    }

    /**
     * 功能描述：角色新建
     * @param iamRoleCreateDto
     * @return int
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int roleCreate(IamRoleCreateDto iamRoleCreateDto) {
        int verifiRoleName = iamRoleManagerMapper.verifiRoleName(iamRoleCreateDto);
        if(verifiRoleName>0){
            throw new RuntimeException("角色名称要求非空唯一，且该角色名称已存在！");
        }
        // 新建角色生成角色编码时加锁，失效时间10秒钟
        RLock lock = redissonClient.getLock(RedisConstants.AUTH_ROLE_CODE_LOCK);
        Long roleCode = null;
        try {
            // 5分钟内重试获取锁，10秒后释放
            boolean isLocked = lock.tryLock(5 * 60 * 1000L, 10 * 1000L, TimeUnit.MILLISECONDS);
            // 获取到锁后，进行自增
            if (!isLocked) {
                log.warn("Route=>IamRoleManagerServiceImpl method=>roleCreate  角色新建获取不到锁，生成角色编码失败:{}", Json.toJsonString(iamRoleCreateDto));
                throw new RuntimeException("获取ROLE_CODE_LOCK锁失败！");
            }
            // 加锁成功后,获取当前缓存值
            String value = RedisUtils.get(envName + RedisConstants.AUTH_ROLE_CODE);
            // 若为空则从数据库取当前最大值+1
            if (StringUtils.isEmpty(value)) {
                roleCode = Long.valueOf(iamRoleManagerMapper.getMaxRoleCode()) + 1;
            } else {
                // 当前缓存获取成功，并加新增用户数量入缓存
                roleCode = Long.parseLong(value) + 1;
            }
            // 将最新值更新到缓存中
            RedisUtils.set(envName + RedisConstants.AUTH_ROLE_CODE, String.valueOf(roleCode));
        } catch (Exception e) {
            log.error("Route=>IamRoleManagerServiceImpl method=>roleCreate 角色新建异常:{}, 入参为:{}", e.getMessage(),Json.toJsonString(iamRoleCreateDto));
            throw new RuntimeException("角色新建异常!");
        } finally {
            // 手动解锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        AtomicReference<Long> finalCode = new AtomicReference<>(roleCode);
        iamRoleCreateDto.setRoleCode("ROLE_" + String.format("%06d", finalCode.getAndSet(finalCode.get())));
        log.info("Route=>IamRoleManagerServiceImpl method=>roleCreate  角色新建 生成角色编码成功:{}", finalCode.get());
        return iamRoleManagerMapper.roleCreate(iamRoleCreateDto);
    }

    /**
     * 功能描述：角色编辑
     * @param iamRoleCreateDto
     * @return int
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int roleEdit(IamRoleCreateDto iamRoleCreateDto) {
        // 判断角色名称有没有修改
        String newRoleName = iamRoleManagerMapper.queryRoleName(iamRoleCreateDto);
        if(StringUtils.equalsIgnoreCase(newRoleName,iamRoleCreateDto.getRoleName())){
            log.info("Route=>IamRoleManagerServiceImpl method=>roleEdit  角色编辑时没有修改角色名称:{}", iamRoleCreateDto.getRoleName());
        }else{
            log.warn("Route=>IamRoleManagerServiceImpl method=>roleEdit  角色编辑时修改了角色名称:{}", iamRoleCreateDto.getRoleName());
            int verifiRoleName = iamRoleManagerMapper.verifiRoleName(iamRoleCreateDto);
            if(verifiRoleName>0){
                throw new RuntimeException("角色名称要求非空唯一，且该角色名称已存在！");
            }
        }
        return iamRoleManagerMapper.roleEdit(iamRoleCreateDto);
    }

    /**
     * 功能描述：角色删除
     * @param iamRoleDeleteDto
     * @return int
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result roleDelete(IamRoleDeleteDto iamRoleDeleteDto) {
            iamRoleDeleteDto.getRoleCode().forEach(code->{
                // 该角色下的所有用户数量
                int userByRoleCode = iamRoleManagerMapper.queryUserByRoleCode(code,iamRoleDeleteDto.getCompanyCode());
                if(userByRoleCode>0){
                    log.warn("Route=>IamRoleManagerServiceImpl method=>roleDelete 角色刪除: 该角色下绑定的用户数为:{}", userByRoleCode);
                    throw new RuntimeException("该角色存在绑定用户，如需删除该角色，请清空角色下用户!");
                }else{
                    // 删除该角色 todo 条件+普管角色字段
                    boolean roleDelete = iamRoleManagerMapper.roleDelete(iamRoleDeleteDto.getAdminRoleCode(),code,iamRoleDeleteDto.getCompanyCode());
                    if(!roleDelete){
                        throw new RuntimeException("角色删除失败!");
                    }
                    // 删除该角色关联的应用
                    IamRoleApplySystmRelation relation = new IamRoleApplySystmRelation();
                    relation.setRoleCode(code);
                    relation.setCompanyCode(iamRoleDeleteDto.getCompanyCode());
                    int deleteRoleApplySystem = iamRoleApplySystmRelationMapper.deleteRoleApplySystmRelation(relation);
//                    if(0==deleteRoleApplySystem){
//                        throw new RuntimeException("角色删除,删除该角色关联的应用失败!");
//                    }
                    // 删除该角色关联的菜单和按钮
                    IamRoleAuthorizedApplicationDto iamRoleAuthorized = new IamRoleAuthorizedApplicationDto();
                    iamRoleAuthorized.setRoleCode(code);
                    iamRoleAuthorized.setCompanyCode(iamRoleDeleteDto.getCompanyCode());
                    // ① 删除菜单
                    int deleteRoleMenu = iamRoleMenuRelationMapper.deleteRoleMenuRelation(iamRoleAuthorized);
//                    if(0==deleteRoleMenu){
//                        throw new RuntimeException("角色删除,删除该角色关联的应用菜单失败!");
//                    }
                    // ② 删除按钮
                    int deleteRoleButton = iamRoleButtonRelationMapper.deleteRoleButtonRelation(iamRoleAuthorized);
//                    if(0==deleteRoleButton){
//                        throw new RuntimeException("角色删除,删除该角色关联的应用按钮失败!");
//                    }
                    // 删除该角色关联的组织
                    IamRoleOrgDeleteDto iamRoleOrgDeleteDto = new IamRoleOrgDeleteDto();
                    iamRoleOrgDeleteDto.setRoleCode(code);
                    iamRoleOrgDeleteDto.setCompanyCode(iamRoleDeleteDto.getCompanyCode());
                    int deleteRoleBindOrg = iamRoleOrgFrameworkRelationMapper.deleteRoleBindOrg(iamRoleOrgDeleteDto);
//                    if(0==deleteRoleBindOrg){
//                        throw new RuntimeException("角色删除,删除该角色关联的组织失败!");
//                    }
                }
            });
            return Result.successWithMsg("角色删除成功!");
    }


    /**
     * 功能描述：角色启用或禁用
     * @param iamRoleUpdateStatusDto
     * @return Result
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result updateRoleStatus(IamRoleUpdateStatusDto iamRoleUpdateStatusDto) {
        if (Objects.isNull(iamRoleUpdateStatusDto.getRoleStatus())) {
            return Result.failWithMsg("角色要更改的状态为空！");
        }
        if (CollectionUtils.isEmpty(iamRoleUpdateStatusDto.getRoleCodes())) {
            return Result.failWithMsg("角色编码集合为空！");
        }
        // 批量更新角色状态： 启用/禁用
        int updateRoleStatus = iamRoleManagerMapper.updateRoleStatus(iamRoleUpdateStatusDto);
        if(0==updateRoleStatus){
            return Result.failWithMsg("角色启用或禁用失败！");
        }
        return Result.successWithMsg("角色启用或禁用成功！");
    }

    /**
     * 功能描述：角色关联用户查询——根据用户编码、姓名【可选】，模糊查询某个角色下所有绑定的用户（要实现分页）
     * @param iamRoleQueryUserParam
     * @return List
     */
    @Override
    public PageInfo<IamUserInfoVo> queryRoleBindUsers(IamRoleQueryUserParam iamRoleQueryUserParam) {
        //该角色下所有绑定的用户id集合 todo 加普管角色编码
        List<String> userCodes = iamRoleManagerMapper.queryRoleBindUsers(iamRoleQueryUserParam.getRoleCode(),iamRoleQueryUserParam.getCompanyCode());
        if(CollectionUtils.isEmpty(userCodes)){
            throw new RuntimeException("该角色下未绑定任何用户！");
        }
        iamRoleQueryUserParam.setUserCodes(userCodes);
        Result<PageInfo<IamUserInfoVo>> pageInfoResult = iamRoleManagerFeign.queryRoleBindUsers(iamRoleQueryUserParam);
        PageInfo<IamUserInfoVo> pageInfo = pageInfoResult.getData();
        return pageInfo;
    }

    /**
     * 功能描述：角色移除用户
     * @param iamRoleRemoveUserParame
     * @return int
     */
    @Override
    public int roleRemoveUsers(IamRoleRemoveUserParam iamRoleRemoveUserParame) {
        return iamRoleManagerMapper.roleRemoveUsers(iamRoleRemoveUserParame);
    }

    /**
     * 功能描述：查询该角色下还未绑定的用户（分页展示）
     * @param
     * @return
     */
    @Override
    public PageInfo<IamUserInfoVo> queryRoleNotBindUsers(IamRoleQueryUserParam iamRoleQueryUserParam) {
        //首先查询该角色下已经绑定的用户id集合 todo 加普管角色编码
        List<String> userCodes = iamRoleManagerMapper.queryRoleBindUsers(iamRoleQueryUserParam.getRoleCode(),iamRoleQueryUserParam.getCompanyCode());
        if(CollectionUtils.isEmpty(userCodes)){
            log.info("Route==>IamRoleManagerController method=queryRoleNotBindUsers 该角色下目前暂未绑定任何用户！");
        }
        iamRoleQueryUserParam.setUserCodes(userCodes);
        //查询该角色下还未绑定的用户 // todo 限制用户组织范围
        Result<PageInfo<IamUserInfoVo>> pageInfoResult = iamRoleManagerFeign.queryRoleNotBindUsers(iamRoleQueryUserParam);
        PageInfo<IamUserInfoVo> pageInfo = pageInfoResult.getData();
        return pageInfo;
    }

    /**
     * 功能描述：角色绑定用户
     * @param iamRoleBindUserDt0
     * @return int
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int roleBindUsers(IamRoleBindUserDto iamRoleBindUserDt0) {
        iamRoleBindUserDt0.getUserCodes().forEach(userCode->{
            int verifiRoleBindUser = iamRoleManagerMapper.verifiRoleBindUser(iamRoleBindUserDt0.getRoleCode(), userCode);
            if(verifiRoleBindUser>0){
                throw new RuntimeException("该角色下该用户已添加！请选择其他用户");
            }
        });
        return iamRoleManagerMapper.iamRoleBindUserDto(iamRoleBindUserDt0);
    }

    /**
     * 功能描述：角色按组织绑定用户
     * @param iamRoleBindUserByOrgDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void roleBindUsersByOrg(IamRoleBindUserByOrgDto iamRoleBindUserByOrgDto) {
        //新增数据前，先删除该角色已绑定的组织的旧数据
        IamRoleOrgDeleteDto iamRoleOrgDeleteDto=new IamRoleOrgDeleteDto();
        BeanUtils.copyProperties(iamRoleBindUserByOrgDto,iamRoleOrgDeleteDto);
        iamRoleOrgFrameworkRelationMapper.deleteRoleBindOrg(iamRoleOrgDeleteDto);
        // todo 循环逻辑待优化
        iamRoleBindUserByOrgDto.getOrgCodes().forEach(code->{
            if(null!=code.getOrgLevel()){
                //组织编码
                iamRoleBindUserByOrgDto.setOrgCode(code.getCode());
                iamRoleBindUserByOrgDto.setPostId(null);
            }else {
                //岗位编码
                iamRoleBindUserByOrgDto.setPostId(code.getCode());
                iamRoleBindUserByOrgDto.setOrgCode(null);
            }
            //新增数据
            int insert=iamRoleOrgFrameworkRelationMapper.insertRoleBindOrg(iamRoleBindUserByOrgDto);
            if(0==insert) {
                throw new RuntimeException("新增角色组织架构中间关系表数据失败！");
            }
        });
    }

    /**
     * 功能描述：获取某个角色下某个组织树下已经勾选的组织或岗位，回填
     * @param iamOrgInfoAlreadySelectDto
     * @return List
     */
    @Override
    public List<String> getAlreadyOrgThreeInfo(IamOrgInfoAlreadySelectDto iamOrgInfoAlreadySelectDto) {
        List<Map<String, String>> alreadyOrgThreeInfo = iamRoleOrgFrameworkRelationMapper.getAlreadyOrgThreeInfo(iamOrgInfoAlreadySelectDto);
        List<String> list = new ArrayList<>();
        alreadyOrgThreeInfo.forEach(item->{
            if(StringUtils.isNotBlank(item.get("orgCode"))){
                list.add(item.get("orgCode"));
            }
            if(StringUtils.isNotBlank(item.get("postId"))){
                list.add(item.get("postId"));
            }
        });
        return list;
    }

    /**
     * 功能描述：角色授权应用系统
     * @param iamRoleAuthorizedApplicationDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void roleAuthorizedApplication(IamRoleAuthorizedApplicationDto iamRoleAuthorizedApplicationDto){
        //删除按钮旧数据
        iamRoleButtonRelationMapper.deleteRoleButtonRelation(iamRoleAuthorizedApplicationDto);
        //删除菜单旧数据
        iamRoleMenuRelationMapper.deleteRoleMenuRelation(iamRoleAuthorizedApplicationDto);

        //先获取勾选的菜单树（可包含按钮）// todo 循环优化
        iamRoleAuthorizedApplicationDto.getMenuThreeVos().forEach((menuThreeVo) ->{
            iamRoleAuthorizedApplicationDto.setCode(menuThreeVo.getCode());
            // 按照层级关系，来判断是菜单还是按钮
            if(null==menuThreeVo.getMenuLevel()){
                int buttonRelation = iamRoleButtonRelationMapper.insertRoleButtonRelation(iamRoleAuthorizedApplicationDto);
                if(0==buttonRelation){
                    throw new RuntimeException("新增按钮数据失败！");
                }
            } else {
                int menuRelation = iamRoleMenuRelationMapper.insertRoleMenuRelation(iamRoleAuthorizedApplicationDto);
                if(0==menuRelation){
                    throw new RuntimeException("新增菜单数据失败！");
                }
            }
        });
        IamRoleApplySystmRelation applySystmRelation = new IamRoleApplySystmRelation();
        applySystmRelation.setDeleted(0);
        BeanUtils.copyProperties(iamRoleAuthorizedApplicationDto,applySystmRelation);
        //新增前先删除旧数据
        iamRoleApplySystmRelationMapper.deleteRoleApplySystmRelation(applySystmRelation);
        //数据落库
        int insertRoleApplySystmRelation = iamRoleApplySystmRelationMapper.insert(applySystmRelation);
        if(0==insertRoleApplySystmRelation){
            throw new RuntimeException("角色应用系统中间关系表：新增数据失败！");
        }

    }

    /**
     * 功能描述：角色授权应用系统勾选的菜单或按钮，回填
     * @param iamMenuManagerAlreadySelectDto
     * @return List
     */
    @Override
    public Set<String> getAlreadyMenuThreeInfo(IamMenuManagerAlreadySelectDto iamMenuManagerAlreadySelectDto) {
        List<String> buttons = iamRoleButtonRelationMapper.querySelectButton(iamMenuManagerAlreadySelectDto);
        List<String> menus = iamRoleMenuRelationMapper.querySelectMenu(iamMenuManagerAlreadySelectDto);
        Set<String> codes = new TreeSet<>();
        codes.addAll(buttons);
        codes.addAll(menus);
        if(CollectionUtils.isEmpty(codes)){
            return new TreeSet<>();
        }
        return codes;
    }

    /**
     * 功能描述：查看一个用户(账户)的应用详情
     * @param iamApplyDetaislParam
     * @return List
     */ // todo 应用详情：usercode 1查普通用户角色，2：usercode对应的组织查普通用户角色 3：找jt 4:找dt用户组，应用范围，最后取并集
    @Override
    public Result getApplicationDetails(IamApplyDetaislParam iamApplyDetaislParam) {
        // 根据用户id+租户，查询该用户的归属的所有的 角色编码+应用编码
        List<Map<String, String>> mapList = iamRoleManagerMapper.queryApplyCodeByUserCode(iamApplyDetaislParam);
        if(CollectionUtils.isEmpty(mapList)){
            return Result.failWithMsg("根据用户id+租户，查询该用户的归属的所有的 角色编码+应用编码，查询不到任何相关信息");
        }
        // 根据用户id+租户，查询该用户的归属的所有的 用户组+应用编码
        List<Map<String, String>> mapList1 = iamStaticUserGroupManagerMapper.queryApplyCodeByUserCode(iamApplyDetaislParam);
        if(CollectionUtils.isEmpty(mapList1)){
            return Result.failWithMsg("根据用户id+租户，查询该用户的归属的所有的 用户组+应用编码，查询不到任何相关信息");
        }
        // 该账户的所有应用编码集合封装
        Set<String> treeSet = new TreeSet<>();
        mapList.forEach(item->{
            treeSet.add(item.get("applyCode"));
        });
        mapList1.forEach(item1->{
            treeSet.add(item1.get("buttonApplyCode"));
            treeSet.add(item1.get("meunApplyCode"));
        });
        // 根据应用编码，查应用的相关信息
        Result result = iamRoleManagerFeign.queryApplySystemByApplyCode(treeSet,iamApplyDetaislParam.getCompanyCode());
        return Result.success("查询应用编码成功！",Objects.requireNonNull(result.getData()));
    }

    /**
     * 功能描述：查询管理员应用权限列表
     * param：roleCode
     * param：companyCode
     * @return Result
     */
    @Override
    public Result queryAdminRole(String roleCode, String companyCode) {
        Result result = iamRoleManagerFeign.queryAdminRole(roleCode, companyCode);
        if(Objects.isNull(result.getData())) {
           return Result.failWithMsg("查询管理员权限列表数据为空");
        }
        return result;
    }


}
